Knowledge Archive
Summary · AI

深入源码:Hermes Agent 如何实现 Self-Improving

AI 2026-04-29 · 16 min read · 5 backlinks
HermesAgentSelf-ImprovingMemorySkill源码分析阿里云

深入源码:Hermes Agent 如何实现 "Self-Improving"

来源: 阿里云开发者 - 白家杰 原文: https://mp.weixin.qq.com/s/Qi68ptxQRyiA932JU49SYQ 仓库: github.com/NousResearch/hermes-agent

核心观点

Hermes AgentOpenClaw 的根本区别不是功能多少,而是设计哲学的分野:OpenClaw 的 Skill 靠人喂(手写 Markdown),Hermes 的 Skill 自己长(Agent 自动创建+自动修补)。三个子系统(Memory + Skill + Nudge Engine)构成自学习闭环,用得越久能力越强。


第一章:Memory — 越用越懂你

两个文件,全部认知

Memory 设计极其克制——两个纯文本文件,用 § 分隔条目:

bash
~/.hermes/memories/
├── MEMORY.md    # Agent 的个人笔记(环境事实、项目约定、工具怪癖)
└── USER.md      # Agent 对用户的认知(偏好、沟通风格、工作习惯)

硬上限设计:MEMORY 限 2200 chars,USER 限 1375 chars。容量有限迫使 Agent 挑重要的记,过时的自然被挤掉。对比 OpenClaw 的 MEMORY.md 是纯追加模式,用几个月就膨胀成几万行的怪兽文件。

核心实现:

python
# tools/memory_tool.py:116-122
class MemoryStore:
    def __init__(self, memory_char_limit=2200, user_char_limit=1375):
        self.memory_entries: List[str] = []
        self.user_entries: List[str] = []
        self.memory_char_limit = memory_char_limit
        self.user_char_limit = user_char_limit
        self._system_prompt_snapshot: Dict[str, str] = {"memory": "", "user": ""}

超限处理:强迫自我反思

超限后 add 直接失败,返回当前所有条目让模型自己决定删/合并:

python
# tools/memory_tool.py:248-259
if new_total > limit:
    current = self._char_count(target)
    return {
        "success": False,
        "error": (
            f"Memory at {current:,}/{limit:,} chars. "
            f"Adding this entry ({len(content)} chars) would exceed the limit. "
            f"Replace or remove existing entries first."
        ),
        "current_entries": entries,
        "usage": f"{current:,}/{limit:,}",
    }

错误信息中 "Replace or remove existing entries first" 引导模型走向 replaceremove 操作。模型不是被动执行淘汰规则,而是主动做信息整理——这本身就是一次自我反思。

冻结快照机制

每次会话启动时冻结一份快照,之后系统提示词用的都是这份快照:

python
# tools/memory_tool.py:124-140
def load_from_disk(self):
    mem_dir = get_memory_dir()
    self.memory_entries = self._read_file(mem_dir / "MEMORY.md")
    self.user_entries = self._read_file(mem_dir / "USER.md")
    self._system_prompt_snapshot = {
        "memory": self._render_block("memory", self.memory_entries),
        "user": self._render_block("user", self.user_entries),
    }

为什么冻结而非实时更新? 系统提示词会话内不变就能共享前缀缓存(KV Cache),省掉重复计费。新写入只改磁盘,下一个会话才刷新。

提示词引导:声明式事实 vs 命令式指令

python
# agent/prompt_builder.py:144-162
MEMORY_GUIDANCE = (
    "You have persistent memory across sessions. Save durable facts using the memory "
    "tool: user preferences, environment details, tool quirks, and stable conventions.\n"
    "Prioritize what reduces future user steering — the most valuable memory is one "
    "that prevents the user from having to correct or remind you again.\n"
    "Write memories as declarative facts, not instructions to yourself. "
    "'User prefers concise responses' ✓ — 'Always respond concisely' ✗. "
    "'Project uses pytest with xdist' ✓ — 'Run tests with pytest -n 4' ✗."
)

Memory 要求写成声明式事实("User prefers concise responses"),而非命令式指令("Always respond concisely")。前者是偏好,可被当前上下文覆盖;后者是死命令,限制灵活性。

Tool Schema 还规定 "If you've discovered a new way to do something, save it as a skill." —— Memory 不存操作步骤,操作步骤归 Skill 管


第二章:Skill — 把做过的事变成会做的事

Skill 结构

每个 Skill 是一个目录,核心是 SKILL.md:

bash
~/.hermes/skills/
├── devops/
│   └── flask-k8s-deploy/
│       ├── SKILL.md          # 主指令
│       ├── references/       # 参考文档
│       └── templates/        # 模板文件
└── software-development/
    └── fix-pytest-fixtures/
        └── SKILL.md

典型 SKILL.md 包含:When to use(触发条件)、Steps(执行步骤)、Pitfalls(踩坑记录)。Pitfalls 不是预先写好的,而是 Agent 踩坑后追加的

自动创建触发

Agent 不需要用户说"帮我创建 Skill"。驱动力来自 tool schema:

python
# tools/skill_manager_tool.py:681-701
SKILL_MANAGE_SCHEMA = {
    "name": "skill_manage",
    "description": (
        "Create when: complex task succeeded (5+ calls), errors overcome, "
        "user-corrected approach worked, non-trivial workflow discovered, "
        "or user asks you to remember a procedure.\n"
        "Update when: instructions stale/wrong, OS-specific failures, "
        "missing steps or pitfalls found during use. "
        "If you used a skill and hit issues not covered by it, "
        "patch it immediately with skill_manage(action='patch') "
        "— don't wait to be asked."
    ),
}

创建门槛:工具调用超过 5 次(简单任务不记)、踩过坑再修复的经验才有价值、用户纠正过的做法要铭记。

自我修补(patch)

执行 Skill 时发现新坑,完成后回头做精确局部修补:

python
# tools/skill_manager_tool.py:397-485
def _patch_skill(name, old_string, new_string, file_path=None, replace_all=False):
    """Targeted find-and-replace within a skill file."""
    from tools.fuzzy_match import fuzzy_find_and_replace
    new_content, match_count, _strategy, match_error = fuzzy_find_and_replace(
        content, old_string, new_string, replace_all
    )
    # 修改前备份原内容
    original_content = content
    _atomic_write_text(target, new_content)
    # 修改后重新做安全扫描
    scan_error = _security_scan_skill(skill_dir)
    if scan_error:
        _atomic_write_text(target, original_content)  # 不通过就回滚

用 fuzzy_find_and_replace 容忍格式差异,每次修改后跑 _security_scan_skill(),不通过自动回滚。

渐进式加载

Skill 多了不能全塞进系统提示词。Hermes 默认只放轻量索引(名字+一句话描述),Agent 判断相关时才通过 skill_view 加载完整内容。先看目录再翻全文,按需加载。

对比 OpenClaw 的"重型背包"模式:每次会话把 SOUL.md、IDENTITY.md 和各种设定一股脑塞进上下文,设定越多背包越沉,Token 浪费严重,模型注意力被稀释。


第三章:Nudge Engine — 谁来提醒 Agent "该学习了"

Memory 和 Skill 都是存储系统,写入需要有人触发。Nudge Engine 就是这个触发器——运行时维护两个计数器,定时提醒 Agent 该停下来想想。

两个计数器,两种粒度:Memory 的信息来自用户输入,按回合计;Skill 的经验来自工具使用过程,按迭代计。计数器到阈值就触发审查,Agent 主动调用了 memoryskill_manage 则重置。

后台 fork Agent:静默审查

Nudge 触发后不在主对话中插话——在后台 fork 一个独立 Agent 实例,拿着主对话的快照做审查:

  • 输出重定向到 /dev/null,用户完全无感知
  • 最多 8 次工具调用,不会无限消耗 API
  • Review Agent 自身的 nudge 被禁用,避免无限递归
  • 和主 Agent 共享同一份 Memory,写入直接生效

"干活"和"反思"拆成两个实例,互不干扰。 审查在响应发送给用户之后才触发。

Review Agent 靠两套审查提示词:Memory Review 关注用户偏好和个人信息,Skill Review 关注非平凡的解题过程。每个 prompt 都以 "If nothing is worth saving, just say 'Nothing to save.' and stop." 收尾——防止 Review Agent 每次都往里塞东西"交差"。


第四章:完整案例 — 三次会话从"不会"到"精通"

K8s 部署场景的三次会话演进:

维度会话 1(冷启动)会话 2(Skill 复用)会话 3(全协同)
工具调用12 次9 次6 次
错误数210
Memory触发写入系统提示词注入
Skill触发创建复用 + 自我修补复用已修补版本
  • 会话 1:冷启动,靠基座知识摸索,12 次调用踩两个坑。Review Agent 自动创建 Skill
  • 会话 2:加载 Skill 后照步骤做,已知坑被绕过,遇到 Django 新坑。Review Agent 写入用户画像 + 记住 registry 地址 + patch Skill
  • 会话 3:Agent 已知用户身份、环境信息、所有坑点,6 次调用零错误

第五章:安全机制

Agent 能往自己"脑子"里写东西,攻击面随之增大。两层防护:

第一层:Memory 内容扫描 — 因为 Memory 最终注入系统提示词,如果被诱导记住 "ignore all previous instructions",下次会话就被劫持

第二层:Skill 安全扫描 — 自创和 Hub 安装的 Skill 走同一套扫描,不通过就回滚


第六章:设计取舍一览

设计决策表面效果背后考量
Memory 限 2200 chars迫使 Agent 挑重要的记低质量 Memory 注入系统提示词 = 每次 API 调用都带噪声
声明式事实 vs 操作步骤分离Memory 存事实,Skill 存步骤两者更新频率、触发条件、安全风险完全不同
冻结快照模式系统提示词会话内不变保护前缀缓存,避免每轮 API 调用重新计费
后台 fork 审查用户感知不到 review 过程自省不应占用用户任务的 attention budget
Nudge 计数器可配置默认 10太频繁浪费 API 成本,太稀疏错过学习机会
patch 优先于全量重写局部修复 Skill保留已验证的稳定部分,只改需要改的
安全扫描 + 自动回滚拒绝恶意写入Memory/Skill 最终进入系统提示词,是一等安全边界

第七章:RDSHermes — 从开发者工具到团队产品

开源 Hermes 仍是偏开发者的工具。RDSHermes 补齐四件事:

  1. 数据库安全纳管:多引擎一键接入,可设只读模式
  2. 身份认证托管:AK/SK 加密托管,网关代理鉴权,密钥不落盘
  3. 内置数据库专业技能:Skill Hub 预装智能巡检/慢 SQL 诊断/索引优化,解决冷启动
  4. 全链路监控审计:写操作需确认,会话可追溯,Token 消耗可监控

团队场景下 Skill 存储从本地磁盘搬到云端——一个 DBA 踩过的坑,团队里所有人的 Agent 都能绕过。自我进化从单点变为组织级。

关键引用

"OpenClaw 的 Skill 是手写的配置文件,用了一年还是那份手写的配置文件;Hermes 的 Skill 是越用越厚的经验资产——每一次踩坑都在加固护城河。"

"真正的护城河是 Agent 在工作中积累的领域知识。"

"Skills that aren't maintained become liabilities."

关联页面